#pragma once
#include <string>
#include <memory>
#include <vector>
#include <typeinfo>
#include <type_traits>
#include <fmt/format.h>

#ifdef __GNUC__
#define FORCEINLINE [[gnu::always_inline]]
#elif defined(__MSVC__)
#define FORCEINLINE
#endif

class alignas(8) Object {
public:
    Object() { m_children.reserve(1000); };
    virtual ~Object(){};

    Object(const Object &) = delete;
    Object &operator=(const Object &) = delete;
    Object(Object &&other) : m_parent(other.m_parent), m_children(std::move(other.m_children)) {
        other.m_parent = nullptr;
    };
    Object &operator=(Object &&other) {
        m_parent = other.m_parent;
        other.m_parent = nullptr;
        m_children = std::move(other.m_children);
        return *this;
    }

    FORCEINLINE bool isRoot() const { return m_parent == nullptr; }
    FORCEINLINE Object *parent() const { return m_parent; }
    Object *addChild(std::unique_ptr<Object> child) {
        child->m_parent = this;
        m_children.emplace_back(std::move(child));
        return m_children.back().get();
    }
    Object *addChild(Object &&child) {
        child.m_parent = this;
        m_children.emplace_back(std::make_unique<Object>(std::move(child)));
        return m_children.back().get();
    }
    FORCEINLINE Object *childAt(size_t index) const { return m_children.at(index).get(); }

    // User-defined
    virtual void onUpdate(float ms){};

protected:
    virtual void update(float ms) {
        onUpdate(ms);
        updateChildren(ms);
    }
    FORCEINLINE void updateChildren(float ms) {
        for (auto &child : m_children) child->update(ms);
    }

protected:
    Object *m_parent{nullptr};
    std::vector<std::unique_ptr<Object>> m_children{};
};